/*******************************************************************************
 * Copyright (c) 2002 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.core.internal;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import jdbm.JDBMHashtable;
import jdbm.JDBMRecordManager;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.syncinfo.FolderSyncInfo;
import net.sourceforge.perforce.core.syncinfo.ResourceSyncInfo;
import org.eclipse.core.internal.localstore.CoreFileSystemLibrary;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
	
/**
  * @version $Revision: 83 $
  * @author  <a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
public class CacheStore
{
	// --------------------------------------------------------------------- Private Static Constant
	
	/** The CacheStore current version. */
	private static final Integer CURRENT_VERSION = new Integer(1);
	
	// --------------------------------------------------------------------------- Private Variables
	
	/** Stores the JDBM Record Manager. */
	private JDBMRecordManager recordManager;
	/** Stores the cache for ResourceSyncInfos. */
	private JDBMHashtable resourceSyncCache;
	/** Stores the cache for FolderSyncInfos. */
	private JDBMHashtable folderSyncCache;

	// ------------------------------------------------------------------------- Public Constructors
	
	/** Constructor for CacheStore. 
	  * 
	  * @param server the perforce server.
	  */ 
	public CacheStore(IPerforceServer server)
	{
		try
		{
			String filename = 
				server.getPort() + '_' + server.getClient() + '_' + server.getUser();
			filename = filename.replace(':', '_');
			
			IPath path = PerforceCorePlugin.getPlugin().getStateLocation().append(filename);
			
			recordManager = new JDBMRecordManager(path.toOSString());
			JDBMHashtable info = recordManager.getHashtable("info");
			Integer version = (Integer) info.get("version");
			if (version == null || !version.equals(CacheStore.CURRENT_VERSION))
			{
				recordManager.close();
				
				File cacheFile = new File(path.toOSString() + ".db");
				cacheFile.delete();
					
				cacheFile = new File(path.toOSString() + ".lg");
				cacheFile.delete();
				
				recordManager = new JDBMRecordManager(path.toOSString());
				info = recordManager.getHashtable("info");
				info.put("version", CacheStore.CURRENT_VERSION);
			}
							
			resourceSyncCache = recordManager.getHashtable("resourceSyncCache");
			folderSyncCache = recordManager.getHashtable("folderSyncCache");
		}
		catch (IOException e)
		{
			PerforceCorePlugin.logError(e.getMessage(), e);
		}
	}
	
	// ------------------------------------------------------------------------------ Public Methods

	/** Call this method to close the CacheStore. 
	  * 
	  * @throws IOException in case of an I/O error
	  */
	public void close()
		throws IOException
	{
		recordManager.close();
	}

	/** Gets ResourceSyncInfo from cache.
	  * 
	  * @param container the container for which we need the FolderSyncInfo.
	  * @return the folder sync info or null.
	  */	
	public FolderSyncInfo getFolderSyncInfo(IContainer container)
	{
		FolderSyncInfo result = null;
		try
		{
			String key = createCacheKeyFromPath(container.getLocation());
			result = (FolderSyncInfo) folderSyncCache.get(key);
		}
		catch (IOException e)
		{
			PerforceCorePlugin.logError(e.getMessage(), e);
		}
		
		return result;
	}
	
	/** Persistently stores the specified FolderSyncInfo for the given container.
	  *
	  * @param container the container.
	  * @param syncInfo the FolderSyncInfo to store. 
	  * @return the previously stored FolderSyncInfo.
	  */
	public FolderSyncInfo setFolderSyncInfo(IContainer container, FolderSyncInfo syncInfo)
	{
		try
		{
			String key = createCacheKeyFromPath(container.getLocation());
			
			FolderSyncInfo result = getFolderSyncInfo(container); 
			if (syncInfo == null)
			{
				folderSyncCache.remove(key);
			}
			else 
			if (syncInfo != null && !syncInfo.equals(result))
			{
				folderSyncCache.put(key, syncInfo);
			}
			
			return result;
		}
		catch (IOException e)
		{
			PerforceCorePlugin.logError(e.getMessage(), e);
			return null;
		}		
	}
	
	/** Wrapper for fstat().
	  * 
	  * @param container that contains resources for which we need ResourceSyncInfo's.
	  * @return array of ResourceSyncInfo.
	  */		
	public ResourceSyncInfo[] getResourceSyncInfo(IContainer container)
	{
		try
		{
			String key = createCacheKeyFromPath(container.getLocation());
			Map children = (Map) resourceSyncCache.get(key);
			if (children != null)
			{
				return (ResourceSyncInfo[]) children.values().toArray(
					new ResourceSyncInfo[children.size()]);
			}
		}
		catch (IOException e)
		{
			PerforceCorePlugin.logError(e.getMessage(), e);
		}
		
		return new ResourceSyncInfo[0];		
	}	

	/** Puts ResourceSyncInfos into the cache.
	 * 
	 * @param resource the resource.
	 * @param syncInfo the associated ResourceSyncInfo.
	 * @return old sync info.	 
	 */
	public ResourceSyncInfo setResourceSyncInfo(IResource resource, ResourceSyncInfo syncInfo) 
	{		
		ResourceSyncInfo result = null;
		try
		{
			IContainer container = resource.getParent();
			String key = createCacheKeyFromPath(container.getLocation());
			Map children = (Map) resourceSyncCache.get(key);
			if (children != null)
			{
				result = (ResourceSyncInfo) children.get(resource.getName());
				if (syncInfo == null)
				{
					children.remove(resource.getName());
				}
				else
				{
					children.put(resource.getName(), syncInfo);
				}
				resourceSyncCache.put(key, children);								
			}
			else
			{
				children = new HashMap();
				if (syncInfo != null)
				{
					children.put(resource.getName(), syncInfo);
				}
				resourceSyncCache.put(key, children);
			}
			
			return result;
		}
		catch (IOException e)
		{
			PerforceCorePlugin.logError(e.getMessage(), e);
			return null;
		}
	}

	// ----------------------------------------------------------------------------- Private Methods

	/** Creates the (resource sync) cache key from the given path.
	  * 
	  * @param path the path.
	  * @return the cache key.
	  */
	private String createCacheKeyFromPath(IPath path)
	{
		if (CoreFileSystemLibrary.isCaseSensitive())
		{
			return path.toOSString();
		}
		else
		{
			return path.toOSString().toLowerCase();
		}
	}
}